package com.ruoyi.iot.tcp;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.iot.domain.*;
import com.ruoyi.iot.mapper.*;
import com.ruoyi.iot.service.impl.IotModbusService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class DevicePoller {

    @Autowired
    private IotProductConfigMapper productConfigMapper;

    @Autowired
    private IotDeviceMapper deviceMapper;

    @Autowired
    private IotProductAttributeModelMapper productAttributeModelMapper;

    @Autowired
    private IotDeviceDataMapper deviceDataMapper;

    @Autowired
    private IotMasterConfigMapper masterConfigMapper;

    @Autowired
    private IotModbusService modbusService;

    @Autowired
    private RedisCache redisCache;

    /**
     * @Description: 初始化设备轮询
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    @PostConstruct
    public void init() {
        // 启动轮询
        startPolling();
    }

    public void startPolling() {
        IotProductConfig productConfig = new IotProductConfig();
        productConfig.setReportType("passive_collection");
        // 获取所有被动采集的产品,并放入缓存
        List<IotProductConfig> products = productConfigMapper.selectIotProductConfigList(productConfig);
        for (IotProductConfig product : products) {
            // 清理旧的缓存数据
            redisCache.deleteObject(CacheConstants.PRODUCT_KEY + product.getProductId());
            redisCache.deleteObject(CacheConstants.DEVICE_LIST_KEY + product.getProductId());
            redisCache.deleteObject(CacheConstants.ATTRIBUTE_LIST_KEY + product.getProductId());

            redisCache.setCacheObject(CacheConstants.PRODUCT_KEY + product.getProductId(), product);
            IotDevice device = new IotDevice();
            device.setProductId(product.getProductId());
            device.setDelFlag("0");
            List<IotDevice> devices = deviceMapper.selectIotDeviceList(device);
            if (CollUtil.isEmpty(devices)) {
                continue;
            }
            redisCache.setCacheList(CacheConstants.DEVICE_LIST_KEY + product.getProductId(), devices);
            for (IotDevice iotDevice : devices) {
                redisCache.setCacheObject(CacheConstants.DEVICE_KEY + iotDevice.getDeviceId(), iotDevice);
            }
            IotProductAttributeModel productAttributeModel = new IotProductAttributeModel();
            productAttributeModel.setProductId(product.getProductId());
            productAttributeModel.setDelFlag("0");
            List<IotProductAttributeModel> attributes = productAttributeModelMapper.selectIotProductAttributeModelList(productAttributeModel);
            if (CollUtil.isEmpty(attributes)) {
                continue;
            }
            redisCache.setCacheList(CacheConstants.ATTRIBUTE_LIST_KEY + product.getProductId(), attributes);
            for (IotProductAttributeModel attribute : attributes) {
                redisCache.setCacheObject(CacheConstants.ATTRIBUTE_KEY + attribute.getAttributeId(), attribute);
            }

        }
        IotMasterConfig masterConfig = new IotMasterConfig();
        masterConfig.setDelFlag("0");
        masterConfig.setModeType("TCP");
        masterConfig.setState("run");
        List<IotMasterConfig> masterConfigList = masterConfigMapper.selectIotMasterConfigList(masterConfig);
        if (CollUtil.isEmpty(masterConfigList)) {
            return;
        }
        redisCache.deleteObject(CacheConstants.MASTER_LIST_KEY);
        redisCache.setCacheList(CacheConstants.MASTER_LIST_KEY, masterConfigList);
        for (IotMasterConfig master : masterConfigList) {
            redisCache.setCacheObject(CacheConstants.MASTER_KEY + master.getMasterId(), master);
        }

        // 创建一个线程池，处理多个产品的轮询任务
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(masterConfigList.size());

        //每个采集器创建一个线程
        for (IotMasterConfig m : masterConfigList) {
            executorService.scheduleAtFixedRate(() -> {
                try {
                    // 每隔一段时间采集数据
                    modbusService.getDeviceData(m);
                } catch (Exception e) {
                    log.error("定时任务执行失败: {}", e.getMessage());
                }
            }, 0, m.getAcquisitionFrequency(), TimeUnit.MINUTES);
        }


    }

    /**
     * @Description: 轮询产品数据
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    private void pollProductData(IotProductConfig product) {

        List<IotDevice> devices = redisCache.getCacheList(CacheConstants.DEVICE_LIST_KEY + product.getProductId());
        // 获取产品的所有属性
        List<IotProductAttributeModel> attributes = redisCache.getCacheList(CacheConstants.ATTRIBUTE_LIST_KEY + product.getProductId());

        // 循环设备，生成并执行采集命令
        for (IotDevice iotDevice : devices) {
            for (IotProductAttributeModel attribute : attributes) {
                String command = generateCollectionCommand(product, iotDevice, attribute);
                executeCommand(command, iotDevice);
            }
        }
    }

    /**
     * @Description: 生成采集命令
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    private String generateCollectionCommand(IotProductConfig product, IotDevice device, IotProductAttributeModel attribute) {
        // 从产品配置中获取编码脚本
        String encoderScript = product.getEncoderScript();
        if (encoderScript == null || encoderScript.isEmpty()) {
            return null;
        }
        // 通过脚本生成采集命令，传入设备的机器码和属性信息
        try {
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
            // 将设备信息传递给脚本
            engine.put("machineCode", device.getMachineCode());
            // 将属性信息传递给脚本
            engine.put("attributeCode", attribute.getAttributeCode());
            // 执行脚本Encoder
            engine.eval(encoderScript);
            // 调用 JavaScript 函数并获取返回值 调用函数并获取结果
            Object result = engine.eval("generateReport(data, deviceId)");
            // 返回脚本执行的结果
            return JSONUtil.toJsonStr(result);
        } catch (ScriptException e) {
            log.error("编码脚本执行失败: {}", e.getMessage());
            return null;
        }
    }


    /**
     * @Description: 执行采集命令
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    private void executeCommand(String command, IotDevice device) {
        if (command != null && !command.isEmpty()) {
            // 这里模拟执行命令，实际中可以通过发送 TCP 请求或其他方式来执行命令
            System.out.println("执行采集命令: " + command);
            String deviceIp = device.getGatewayIp();
            int devicePort = device.getGatewayPort();
            TcpClient tcpClient = new TcpClient(deviceIp, devicePort);
            // 获取设备数据,这里会尝试连接设备并获取数据
            String responseData = tcpClient.getDeviceData(command);
            if (responseData != null) {
                // 解析数据并存储
                String deviceDataJson = executeScript(responseData, device.getDeviceId());
                storeDeviceData(deviceDataJson, device);
            } else {
                log.error("设备响应数据为空，未成功获取设备数据！设备:{}.命令：{}", JSONUtil.toJsonStr(device), command);
            }
        } else {
            log.error("命令为空，无法执行！");
        }
    }

    /**
     * @Description: 解析并存储数据
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    private void storeDeviceData(String data, IotDevice device) {
        log.info("存储设备数据: {}", data);
/*
报文格式如下：
{
  "reportId": "R1740724491762",
  "deviceCode": "1",
  "attribute": {
    "UAN": 220.00,
    "UBN": 220.10,
    "UCN": 220.10
  }
}
 */
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            DeviceData deviceData = objectMapper.readValue(data, DeviceData.class);
            Map<String, String> attributes = deviceData.getAttribute();
            IotProductConfig product = redisCache.getCacheObject(CacheConstants.PRODUCT_KEY + device.getProductId());

            for (Map.Entry<String, String> entry : attributes.entrySet()) {
                String attributeCode = entry.getKey();
                String dataValue = entry.getValue();
                IotProductAttributeModel attributeModel = redisCache.getCacheObject(CacheConstants.ATTRIBUTE_KEY + attributeCode);
                IotDeviceData deviceDataEntity = new IotDeviceData();
                deviceDataEntity.setDeviceId(device.getDeviceId());
                deviceDataEntity.setDeviceCode(device.getDeviceCode());
                deviceDataEntity.setDeviceName(device.getDeviceName());
                deviceDataEntity.setProductCode(product.getProductCode());
                deviceDataEntity.setProductName(product.getProductName());
                deviceDataEntity.setAttributeCode(attributeModel.getAttributeCode());
                deviceDataEntity.setAttributeName(attributeModel.getAttributeName());
                deviceDataEntity.setAttributeType(attributeModel.getAttributeType());
                deviceDataEntity.setAttributeUnit(attributeModel.getUnit());
                deviceDataEntity.setDataValue(dataValue);
                deviceDataEntity.setReportTime(LocalDateTime.now());
                // 填充其他字段并保存到数据库
                deviceDataMapper.insertIotDeviceData(deviceDataEntity);
            }
        } catch (IOException e) {
            log.error("解析设备数据失败: " + e.getMessage());
        }
    }


    /**
     * @Description: 执行脚本
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/2/28 上午9:45
     **/
    public String executeScript(String data, Long deviceId) {
        try {
            // 创建 JavaScript 引擎
            ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
            IotDevice device = redisCache.getCacheObject(CacheConstants.DEVICE_KEY + deviceId);
            IotProductConfig product = redisCache.getCacheObject(CacheConstants.PRODUCT_KEY + device.getProductId());
            // 将参数传递给 JavaScript 引擎
            engine.put("data", data);  // 假设你的设备数据存储在 device 中
            engine.put("deviceId", deviceId);  // 设备编码
            String script = product.getParseScript();
            // 执行脚本
            engine.eval(script);  // 执行脚本
            // 调用 JavaScript 函数并获取返回值 调用函数并获取结果
            Object result = engine.eval("generateReport(data, deviceId)");
            return JSONUtil.toJsonStr(result);  // 返回脚本执行的结果
        } catch (ScriptException e) {
            log.error("执行脚本失败: " + e.getMessage());
            return null;
        }
    }
}